﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;

namespace CompanySearchConsole
{

    public class MongoTestContext : CompanySearchConsole.ITestContext
    {
        MongoDatabase clientMongoDb;


        public MongoTestContext(MongoDatabase mongoDatabase)
        {
            // TODO: Complete member initialization
            this.clientMongoDb = mongoDatabase;
        }
        public MongoEntities.RawQueryResult TestRaw(string textToMatch, string discriminator, MongoEntities.Session session)
        {


            var testName = "raw_nameIndex";

            var queryResult = new MongoEntities.RawQueryResult { Name = testName, Session = session, Info = textToMatch };

            var dbCollection = clientMongoDb.GetCollection<MongoEntities.Company>(typeof(MongoEntities.Company).Name);
            dbCollection.EnsureIndex(new IndexKeysBuilder().Ascending("Name"));


            var dbQueriableComany = dbCollection.AsQueryable<MongoEntities.Company>();

            var taskCompaniesById = Task.Factory.StartNew(() =>
            {
                return GetCompanyByAttribute(textToMatch, clientMongoDb);
            });


            var startsWithRegex = new Regex(string.Format("^{0}.*", Regex.Escape(textToMatch)));
            var containsNotStartingRegex = new Regex(string.Format("^(?!{0}).*{0}.*", Regex.Escape(textToMatch)));

            //find all companies that have Name starting with given text, ordered by Name

            //var query = (from c in dbQueriableComany
            //                                           where startsWithRegex.IsMatch(c.Name)
            //                                           orderby c.Name
            //                                           select c).Explain(false);



            queryResult.StartsWith = Execute((from c in dbQueriableComany
                                              where startsWithRegex.IsMatch(c.Name)
                                              orderby c.Name
                                              select c.Id).Take(20).Explain(false));

            queryResult.Contains = Execute((from c in dbQueriableComany
                                            where containsNotStartingRegex.IsMatch(c.Name)
                                            orderby c.Name
                                            select c.Id).Take(20).Explain(false));

            queryResult.GetByAttributes = taskCompaniesById.Result;


            queryResult.Duration = queryResult.StartsWith.Duration + queryResult.Contains.Duration + queryResult.GetByAttributes.Duration;
            queryResult.Discriminator = discriminator;

            return queryResult;
        }



        private static MongoEntities.PartialResult GetCompanyByAttribute(string textToMatch, MongoDatabase clientMongoDb)
        {


            var results = new List<MongoEntities.PartialResult>(3);
            var res = Execute(
                (from isin in clientMongoDb.GetCollection<MongoEntities.Isin>("Isin").AsQueryable()
                 where isin.Value == textToMatch
                 select isin.CompanyId).Explain(false), "isin");
            results.Add(res);

            res = Execute(
                (from item in clientMongoDb.GetCollection<MongoEntities.Ticker>("Ticker").AsQueryable()
                 where item.Value == textToMatch
                 select item.CompanyId).Explain(false), "ticker");
            results.Add(res);

            res = Execute(
                (from item in clientMongoDb.GetCollection<MongoEntities.Sedol>("Sedol").AsQueryable()
                 where item.Value == textToMatch
                 select item.CompanyId).Explain(false), "sedol");
            results.Add(res);



            var result = new MongoEntities.PartialResult { Info = "getByAttributes", PartialResults = results };
            result.Duration = (from a in results select a.Duration).Sum();

            return result;

        }



        public static MongoEntities.PartialResult Execute(Func<int> action, string info = null)
        {

            Stopwatch watch = new Stopwatch();
            watch.Start();
            var count = action();
            watch.Stop();
            var result = new MongoEntities.PartialResult { Duration = watch.ElapsedMilliseconds, Count = count, Info = info };
            return result;
        }


        private static MongoEntities.PartialResult Execute(MongoDB.Bson.BsonDocument bsonDocument, string info = "")
        {

            var cursor = bsonDocument.GetElement("cursor").Value.AsString;
            var n = bsonDocument.GetElement("n").Value.AsInt32;
            var nscanned = bsonDocument.GetElement("nscanned").Value.AsInt32;
            var scanAndOrder = bsonDocument.GetElement("scanAndOrder").Value.AsBoolean;

            var duration = bsonDocument.GetElement("millis").Value.AsInt32;


            var result = new MongoEntities.PartialResult { Duration = duration, Count = n, Info = info, Cursor = cursor, NScanned = nscanned, ScanAndOrder = scanAndOrder };

            return result;
        }



    }
}
